package com.wang.openApigateway.filter;

import com.wang.openApiCommon.model.entity.InterfaceModel;
import com.wang.openApiCommon.model.entity.UserModel;
import com.wang.openApiCommon.remote.InnerInterfaceIntoService;
import com.wang.openApiCommon.remote.InnerUserInterfaceIntoService;
import com.wang.openApiCommon.remote.InnerUserService;
import com.wang.openApiclientsdk.utils.SignUtils;
import com.wang.openApigateway.inservice.ConvertRequestModel;
import com.wang.openApigateway.inservice.LogService;
import com.wang.openApigateway.model.FilterRequestModel;
import lombok.extern.slf4j.Slf4j;
import org.reactivestreams.Publisher;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

@Slf4j
@Component
public class CustomGlobalFilter implements GlobalFilter, Ordered {
    //白名单  todo 后期应该给白名单单独建一个表
    public static final List<String> IP_WHITE_LISt = Arrays.asList("localhost");

    @Resource
    private InnerUserService innerUserService;

    @Resource
    private InnerInterfaceIntoService innerInterfaceInfoService;

    @Resource
    private InnerUserInterfaceIntoService innerUserInterfaceInfoService;

    @Resource
    private LogService logService;

    @Resource
    private ConvertRequestModel convertRequestModel;


    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        log.info("custom global filter");

        //打印参数
        ServerHttpRequest request = exchange.getRequest();
        logService.printExchangeParamLog(request);
        HttpHeaders headers = request.getHeaders();
        logService.printHeaderParamLog(headers);

        //获取参数
        FilterRequestModel filterRequestModel = convertRequestModel.convertModel(request, headers);
        //相当于响应头
        ServerHttpResponse response = exchange.getResponse();

        // 访问控制， 黑白名单
        if (!IP_WHITE_LISt.contains(filterRequestModel.getSourceAddress())) {
            response.setStatusCode(HttpStatus.FORBIDDEN);
            log.info("请求主机地址不在白名单内，拒绝访问，访问host：{}", filterRequestModel.getSourceAddress());
            return response.setComplete();
        }

        UserModel invokeUser = null;
        try {
            invokeUser = innerUserService.getInvokeUser(filterRequestModel.getAccessKey());
        } catch (Exception e) {
            log.error("getInvokeUser error", e);
        }

        if (invokeUser == null) {
            return handleNoAuth(response);
        }

        //已经 innerUserService.getInvokeUser(accessKey);方法里面校验了
//        if (!accessKey.equals(invokeUser.getAccessKey())) {
//            log.info("ak验证失败");
//            return handleNoAuth(response);
//        }

        //校验随机数
//        if (Long.parseLong(nonce) > 10000L) {
//            log.info("随机数验证失败");
//            return handleNoAuth(response);
//        }

        //校验时间戳  时间差大于五分钟就报错
        Long currentTime = System.currentTimeMillis() / 1000;
        Long FIVE_MILLIS = 60 * 5L;
        if ((currentTime - Long.parseLong(filterRequestModel.getTimestamp())) >= FIVE_MILLIS) {
            log.info("时间戳验证失败");
            return handleNoAuth(response);
        }

        // 拿到自己的sign  自己的secretKey也是应该从数据库中查出来的
        //todo 当body存在中文，会乱码，生成的签名就和前端不一致 待解决

        String secretKey = invokeUser.getSecretKey();
        log.info("secretKey", secretKey);
        String mySign = SignUtils.getSign(filterRequestModel.getBody(), secretKey);
        log.info("mySign " + mySign);
        if (!mySign.equals(filterRequestModel.getSign())) {
            log.info("sign验证失败");
            return handleNoAuth(response);
        }

        // 请求的模拟接口是否存在
        // 从数据中查询模拟接口是否存在，以及请求方法是否匹配（还是可以校验请求参数）
        InterfaceModel interfaceInfo = null;
        try {
            interfaceInfo = innerInterfaceInfoService.getInterfaceInfo(filterRequestModel.getPath(), filterRequestModel.getMethod());
        } catch (Exception e) {
            log.error("getInterfaceInfo error", e);
        }
        if (invokeUser == null) {
            return handleNoAuth(response);
        }

        // 是否还有调用次数
        Integer count = null;
        try {
            count = innerUserInterfaceInfoService.getInvokeUserCount(interfaceInfo.getId(), invokeUser.getId());
        } catch (Exception e) {
            log.error("getInvokeUserCount error", e);
        }
        if (count <= 0) {
            log.info("用户没有调用次数");
            return handleNoAuth(response);
        }


        log.info("校验成功，调用实际接口");
        return handleResponse(exchange, chain, interfaceInfo.getId(), invokeUser.getId());

    }

    @Override
    public int getOrder() {
        return -1;
    }

    /**
     * 处理响应
     *
     * @param exchange
     * @param chain
     * @return
     */
    public Mono<Void> handleResponse(ServerWebExchange exchange, GatewayFilterChain chain, Long interfaceinfoId, Long userId) {
        try {
            //从交换器拿响应对象
            ServerHttpResponse originalResponse = exchange.getResponse();

            //缓冲区工厂，拿到缓存数据
            DataBufferFactory bufferFactory = originalResponse.bufferFactory();

            //拿到响应码
            HttpStatus statusCode = originalResponse.getStatusCode();

            if (statusCode == HttpStatus.OK) {
                //装饰，增强能力
                ServerHttpResponseDecorator decoratedResponse = new ServerHttpResponseDecorator(originalResponse) {
                    //等调用完转发的接口后才会执行
                    @Override
                    public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
                        log.info("body instanceof Flux: {}", (body instanceof Flux));
                        if (body instanceof Flux) {
                            //拿到真正的body
                            Flux<? extends DataBuffer> fluxBody = Flux.from(body);
                            //往返回值里写数据
                            //拼接字符串
                            return super.writeWith(fluxBody.map(dataBuffer -> {
                                //8. 调用成功 接口调用次数+1 invokeCount
                                try {
                                    innerUserInterfaceInfoService.invokeCount(interfaceinfoId, userId);
                                } catch (Exception e) {
                                    log.error("invokeCount error", e);
                                }
                                byte[] content = new byte[dataBuffer.readableByteCount()];
                                dataBuffer.read(content);
                                DataBufferUtils.release(dataBuffer);//释放掉内存
                                // 构建日志
                                StringBuilder sb2 = new StringBuilder(200);
                                List<Object> rspArgs = new ArrayList<>();
                                rspArgs.add(originalResponse.getStatusCode());
                                String data = new String(content, StandardCharsets.UTF_8);//data
                                sb2.append(data);
                                log.info("响应结果： ", data);
                                return bufferFactory.wrap(content);
                            }));
                        } else {
                            //9. 调用失败，返回规范错误码
                            log.error("<--- {} 响应code异常", getStatusCode());
                        }
                        return super.writeWith(body);
                    }
                };
                //设置response 对象为装饰过的
                return chain.filter(exchange.mutate().response(decoratedResponse).build());
            }
            return chain.filter(exchange);//降级处理返回数据
        } catch (Exception e) {
            log.error("网关处理响应异常" + e);
            return chain.filter(exchange);
        }
    }


    public Mono<Void> handleNoAuth(ServerHttpResponse response) {
        response.setStatusCode(HttpStatus.FORBIDDEN);
        return response.setComplete();
    }

    public Mono<Void> handleInvokeError(ServerHttpResponse response) {
        response.setStatusCode(HttpStatus.INTERNAL_SERVER_ERROR);
        return response.setComplete();
    }
}